#ifndef MATMUL_H_
#define MATMUL_H_

#include <pthread.h>
#include <string.h>

typedef enum MatMulResult_t {
	SUCCESS,
        ALLOC_FAILURE,
        FAILURE
} MatMulResult;

typedef int ** Mat;
typedef  pthread_mutex_t** MutexMatrix;

typedef struct blockID {
	int IDRow;
	int IDCol;
} BlockID;

typedef struct dimentions {
	int n;
	int m;
	int o;
} Dimentions;

typedef struct job {
	int N_Offset;
	int M_Offset;
	int O_Offset;
	Dimentions dimentions;
} Job;

typedef struct queue {
	pthread_mutex_t queueMutex;
	int size;
	int iterator; //"points" to the next *empty* cell in the array. (iterator == size) => array is full.
	Job* jobs;

} * Queue;

typedef struct argStruct{
	Queue queue;
	Mat matA;
	Mat matB;
	Mat matC;
        int threadId;
} ArgStruct;

/*
 * Description: Multiplies matrixes. Isn't in charge of alocation.
 * Input: matA N*M, matB N*O, empty matC N*O
 * Output: filled matC N*O
 * Errors:
 */
int mulMatrix(Mat mat1, Mat mat2, Mat matRes, int N_Off, int M_Off, int O_Off, int nSize, int mSize, int oSize);


/*
 * Description: Reads
 * Input: matA N*M, matB N*O, empty matC N*O
 * Output: filled matC N*O
 * Errors:
 */
void parser(Mat matA, Mat matB, int N, int M, int O);



/*
 * Description: defines jobs by dividing matrix into blocks, fills queue
 * Input: N, M, O, numOfThreads, empty queue
 * Output: filled queue
 */
void divideToJobs(int N, int M, int O, int numOfThreads, Queue queue);


/*
 * Description: returns the index of the relevant lock for this job
 * Input: the job struct
 * Output: the index
 */
BlockID getBlockID(Job job);

/*
 * Description: dequeues jobs and runs them
 * Input: queue and the matrixes
 * input params in array in the following order: Queue * queue, Mat matA, Mat matB, Mat matC
 */
void threadRun (Queue queue, Mat matA, Mat matB, Mat matC, int threadId);
//while there are jobs in queue, lock and dequeue job, call doJob. if empty do exit

/*
 * Description: calls mult_matrix for the blocks specified for job
 * and calls updateMatrix
 * Input: a job to do, and the matrixes
 * Output: the MatC is updated
 */
MatMulResult doJob(Job job, Mat matA, Mat matB, Mat matC, int threadId);
//allocate & free a matrix to hold the result of calculations

/*
 * Description: adds the values in the block to the values in the matching cells in C
 * Input: the finished job object, the result matrix and the computed block.
 * Output: the MatC is updated
 */
void updateMatrix (Job job, Mat blockToAdd, Mat MatC);


void print_output(Mat matA, Mat matB, Mat matC, int N, int M, int O);

void _initToZeros(Mat mat, int a, int b);
MatMulResult _allocMatrix(Mat* mat, int rows, int cols);


#endif /* MATMUL_H_ */
